﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufPreferenceEntries.h"
#include "GcmpApiDeclaration.h"

namespace gnuf {
    class PreferenceConfiguration;
    class GNUF_BASE_EXPORT Preferences final
        : public gcmp::WeakReferenceable
    {
        CANNOT_COPY_OR_ASSIGN(Preferences)
    public:
        /// \brief Constructor.
        /// \param isMajor: A flag to show if it is a major or a minor.
        /// \param originalFileName: The original configuration file name.
        /// \param modifiedFileName: The modified configuration file name.
        explicit Preferences(
            bool isMajor,
            const wchar_t * originalFileName,
            const wchar_t * modifiedFileName);
        ~Preferences();

        static Preferences * majorPreferences();
        static Preferences * minorPreferences();

        /// \brief Get if it is a major.
        bool isMajor() const { return m_isMajor; }
        /// \brief Get if it is a minor.
        bool isMinor() const { return !m_isMajor; }

        /// \brief Initialize.
        bool initialize();

        /// \brief Add a preference entry group.
        PreferenceEntryGroup * addGroup();
        
        typedef std::list< gcmp::OwnerPtr< PreferenceEntryGroup > > Groups;
        /// \return Return all preference entry groups.
        const Preferences::Groups & groups() const { return m_groups; }
        Preferences::Groups & rGroups() { return m_groups; }

        /// \return Return the group at the index.
        const PreferenceEntryGroup * groupAt(const size_t index) const;
              PreferenceEntryGroup * groupAt(const size_t index);

        /// \return Return the group by the key.
        const PreferenceEntryGroup * groupBy(const std::wstring & key) const;
              PreferenceEntryGroup * groupBy(const std::wstring & key);

        /// \return Return first preference entry whose type is the given type.
        const PreferenceEntry * entryBy(const PreferenceEntry::EType) const;
              PreferenceEntry * entryBy(const PreferenceEntry::EType);

        /// \return Return first preference entry whose key is the given key.
        const PreferenceEntry * entryBy(const std::wstring & key) const;
              PreferenceEntry * entryBy(const std::wstring & key);

        /// \brief Remove a preference entry.
        /// \param key: the key of the preference entry.
        bool removeEntry(const std::wstring & key);

        /// \brief Clear those prefernece entries that the client doesn't need any more.
        void clearEntries(const std::function<bool(const PreferenceEntry *)> & wantToRemove);

        /// \brief Make the snap shots for every entries.
        void makeSnapshots();

        /// \brief Erase the snap shots.
        void eraseSnapshots(bool wantToWriteBack = false);

        /// \brief Save if changed.
        void save();

        /// \brief Persist the preferens into the disk.
        /// \return Return true if successful.
        bool persist();

        /// \return Return true if changed. Otherwise, return false.
        bool isChanged() const { return !m_changed.empty(); }

        /// \return Return all changed preference entries.
        typedef std::list< const PreferenceEntry * > Changed;
        const Preferences::Changed & changed() const { return m_changed; }

        /// \return Return first changed preference entry whose type is the given type.
        const PreferenceEntry * changedEntryBy(PreferenceEntry::EType) const;

        /// \return Return first changed preference entry whose key is the given key.
        const PreferenceEntry * changedEntryBy(const std::wstring & key) const;
        
        enum class EIOState {
            eIdle,
            // Reading
            eReadingTheOriginal,
            eReadingTheModified,
            // Writing
            eWritingTheModified
        };
        ///\return Return the IO state.
        Preferences::EIOState ioState() const { return m_ioState; }

        // For an internal logic.
        bool onPreRead(const PreferenceConfiguration &) const;

        ///////////////////////////////////////////////////////////////////////////////////////////
        // Obsolete some methods. The GNUF will remove them, when get time. -frank 2023.05.22.
        PreferenceEntry * rEntryBy(const std::wstring & key)            { return entryBy(key); }
        PreferenceEntry * rEntryBy(const PreferenceEntry::EType type)   { return entryBy(type); }
        ///////////////////////////////////////////////////////////////////////////////////////////

    private:
        const bool m_isMajor;
        const std::wstring m_originalFileName;
        const std::wstring m_modifiedFileName;
        Preferences::Groups m_groups;
        Preferences::Changed m_changed;
        std::list< gcmp::OwnerPtr< PreferenceEntrySnapshot > > m_snapshots;
        Preferences::EIOState m_ioState = Preferences::EIOState::eIdle;
        std::string m_xmlVersion, m_version;
    };

}
